package front_chat_api

import (
	"blog_go/global"
	"blog_go/models"
	"blog_go/models/ctype"
	"blog_go/utils"
	"errors"
	"github.com/google/uuid"
	"github.com/gorilla/websocket"
	"strings"
	"sync"
)

type Connection struct {
	wsConn *websocket.Conn
	// 读取websocket的channel
	inChan chan []byte
	// 给websocket写消息的channel
	outChan chan []byte
	// 保存到db的channel
	dbChan    chan ChatGroupRsp
	closeChan chan byte
	mutex     sync.Mutex
	// closeChan 状态
	isClosed bool
	// 当前连接的唯一id
	id string
}

// 初始化长连接
func InitConnection(wsConn *websocket.Conn) (conn *Connection, err error) {
	conn = &Connection{
		wsConn:    wsConn,
		inChan:    make(chan []byte, 1000),
		outChan:   make(chan []byte, 1000),
		dbChan:    make(chan ChatGroupRsp, 200),
		closeChan: make(chan byte),
		id:        uuid.NewString(),
	}
	// 启动读协程
	go conn.readLoop()
	// 启动写协程
	go conn.writeLoop()
	// 启动协程
	go conn.saveDBLoop()
	return
}

// 读取websocket消息
func (conn *Connection) ReadMessage() (data []byte, err error) {
	select {
	case data = <-conn.inChan:
	case <-conn.closeChan:
		err = errors.New("connection is closed")
	}
	return
}

// 发送消息到websocket
func (conn *Connection) WriteMessage(data []byte) (err error) {
	select {
	case conn.outChan <- data:
	case <-conn.closeChan:
		err = errors.New("connection is closed")
	}
	return
}

// 关闭连接
func (conn *Connection) Close() {
	// 线程安全的Close,可重入
	conn.wsConn.Close()
	// 只执行一次
	conn.mutex.Lock()
	if !conn.isClosed {
		// 保证只关闭一次 多次关闭会错误
		close(conn.closeChan)
		conn.isClosed = true
	}
	conn.mutex.Unlock()
}

func (conn *Connection) readLoop() {
	var (
		data []byte
		err  error
	)
	for {
		if _, data, err = conn.wsConn.ReadMessage(); err != nil {
			goto ERR
		}
		// 如果数据量过大阻塞在这里,等待inChan有空闲的位置！
		select {
		case conn.inChan <- data:
		case <-conn.closeChan:
			// closeChan关闭的时候
			goto ERR

		}
	}
ERR:
	conn.Close()
}

func (conn *Connection) writeLoop() {
	var (
		data []byte
		err  error
	)
	for {
		select {
		case data = <-conn.outChan:
		case <-conn.closeChan:
			goto ERR
		}
		if err = conn.wsConn.WriteMessage(websocket.TextMessage, data); err != nil {
			goto ERR
		}
	}
ERR:
	conn.Close()
}

func (conn *Connection) saveDBLoop() {
	for {
		select {
		case data := <-conn.dbChan:
			saveChat(data, conn)
		case <-conn.closeChan:
			goto ERR
		}

	}
ERR:
	conn.Close()
}

// 保存聊天信息
func saveChat(rsp ChatGroupRsp, conn *Connection) {
	ip := conn.GetIp()
	addr := utils.GetAddr(ip)
	isGroup := true
	if rsp.MsgType == ctype.Other {
		isGroup = false
	}
	global.DB.Create(&models.ChatModel{
		NickName: rsp.NickName,
		Avatar:   rsp.Avatar,
		Content:  rsp.Content,
		IP:       ip,
		Addr:     addr,
		IsGroup:  &isGroup,
		MsgType:  rsp.MsgType,
	})
}

// 获取ip和地址
func (conn *Connection) GetIp() (ip string) {
	_ip := conn.wsConn.RemoteAddr().String()
	ip = strings.Split(_ip, ":")[0]
	return
}

func (conn *Connection) SaveDBChat(rsp ChatGroupRsp) {
	select {
	case conn.dbChan <- rsp:
	case <-conn.closeChan:
	}
}
